package com.redhat.auth.ftp.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import com.redhat.auth.ftp.FtpUtil;
import com.redhat.auth.ftp.exception.FtpException;
import com.redhat.auth.ftp.util.ZipAndUnzip;

public class FtpUtilImpl implements FtpUtil {

	private FTPClient ftpClient =  new FTPClient();
	
	private String serverIp;
	
	private int port;
	
	private String username;
	
	private String password;
	
	private String encoding;
	
	
	public FtpUtilImpl(String serverIp,int port,String username,String password,String encoding){
		this.serverIp = serverIp;
		this.port = port;
		this.username = username;
		this.password = password;
		this.encoding = encoding;
	}
	
	public File downLoadFile(String path, String filename) throws FtpException {
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		File file = null;
		FileOutputStream fos = null;
		try {
			ftpClient.cwd("/");
			ftpClient.cwd(path);
			file = new File(System.getProperty("java.io.tmpdir")+filename);
			fos = new FileOutputStream(file);
			ftpClient.retrieveFile(filename, fos);
		} catch (IOException e) {
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		} finally {
				try {
					if (fos != null){
						fos.flush();
						fos.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		return file;
	}

	
	public void renameFTPFile(String fromName,String toName) throws FtpException{
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		try {
			ftpClient.rename(fromName, toName);
		} catch (IOException e) {
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}
	}
	
	public List<String> listFiles(String path) throws FtpException{
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		List<String> fileNames = new ArrayList<String>();
		try { 
			FTPFile[] files = ftpClient.listFiles(path);
			for(FTPFile file:files){
				if(file.isFile())fileNames.add(file.getName());
			}
		} catch (IOException e) {
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}
		return fileNames;
	}
	
	public List<String> listDirectory(String path) throws FtpException{
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		List<String> fileNames = new ArrayList<String>();
		try {
			FTPFile[] files = ftpClient.listFiles(path);
			for(FTPFile file:files){
				if(file.isDirectory())fileNames.add(file.getName());
			}
		} catch (IOException e) {
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}
		return fileNames;
	}
	
	/**
	 * 返回一个目录下所有的目录或文件
	 * @param path
	 * @return
	 * @throws FtpException
	 */
	public List<FTPFile> listFTPFiles(String path) throws FtpException{
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		List<FTPFile> ftpFiles = new ArrayList<FTPFile>();
		FTPFile[] files = null;
		try {
			files = ftpClient.listFiles(path);
			for(FTPFile ftpFile:files){
				ftpFiles.add(ftpFile);
			}
		} catch (IOException e) {
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}
		return ftpFiles;
	}
	
	
	/**
	 * 删除FTP上的一个文件
	 * @param path
	 * @throws FtpException
	 */
	public boolean deleteFile(String filePath) throws FtpException{
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		boolean dele = false;
		try {
			dele = ftpClient.deleteFile(filePath);
		} catch (IOException e) {
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}
		return dele;
	}
	
	
	/**
	 * 删除FTP上是目录
	 * @param path
	 * @throws FtpException
	 */
	public boolean deleteDirectory(String dirPath) throws FtpException{
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		boolean dele = false;
		try {
			dele = ftpClient.removeDirectory(dirPath);
		} catch (IOException e) {
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}
		return dele;
	}
	
	public void updateFile(String path, File file) throws FtpException {
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		FileInputStream fis = null;
		boolean  binary = false;
		try {
			ftpClient.cwd("/");
			int code = ftpClient.cwd(path);
			if(code==550){
				ftpClient.makeDirectory(path);
				ftpClient.cwd(path);
			}
			if(file.getName().endsWith("rar") || file.getName().endsWith("zip")){
				ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
				binary = true;
			}
			fis = new FileInputStream(file);
			ftpClient.storeFile(file.getName(), fis);
			if(binary)ftpClient.setFileType(FTP.ASCII_FILE_TYPE);
		} catch (IOException e) {
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}finally{
				try {
					if(fis!=null){fis.close();}
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
	
	public void updateFile(String path, File file,String fileName) throws FtpException {
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		FileInputStream fis = null;
		boolean binary = false;
		try {
			ftpClient.cwd("/");
			int code = ftpClient.cwd(path);
			if(code==550){
				ftpClient.makeDirectory(path);
				ftpClient.cwd(path);
			}
			if(file.getName().endsWith("rar") || file.getName().endsWith("zip")){
				ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
				binary = true;
			}
			fis = new FileInputStream(file);
			ftpClient.storeFile(fileName, fis);
			if(binary)ftpClient.setFileType(FTP.ASCII_FILE_TYPE);
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
				try {
					if(fis!=null){fis.close();}
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
	
	/**
	 * 上传一个ZIP文件，并进行解压
	 * @param path
	 * @param file
	 * @throws FtpException
	 */
	public void updateZipFile(String path,File file,String fileName) throws FtpException{
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		FileInputStream fis = null;
		try {
			ftpClient.cwd("/");
			ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
			int code = ftpClient.cwd(path);
			if(code==550){
				ftpClient.makeDirectory(path);
				ftpClient.cwd(path);
			}
			String tmpPath = System.getProperty("java.io.tmpdir")+file.getName();
			System.out.println(tmpPath);
			ZipAndUnzip zipUtil = new ZipAndUnzip();
			zipUtil.unZipFile(file, tmpPath);
			File uploadFile = new File(tmpPath);
			this.updateDirFiles(uploadFile, path, fileName);
			clearTmpFile(uploadFile);
			ftpClient.setFileType(FTP.ASCII_FILE_TYPE);
		} catch (IOException e) {
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}finally{
				try {
					if(fis!=null){fis.close();}
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}

	/**
	 * 
	 * @param file
	 * @param path
	 * @throws FtpException 
	 */
	public void updateDirFiles(File file,String path,String dirName) throws FtpException{
		String  sep=System.getProperty("file.separator"); 
		String remoteDir = path + "/" + dirName;
		this.makeDirectory(remoteDir);
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		File[] files = file.listFiles();
		for(File uploadFile:files){
			if(uploadFile.isDirectory())updateDirFiles(uploadFile,remoteDir,uploadFile.getName());
			else if(uploadFile.isFile())this.updateFile(remoteDir, uploadFile);
		}
	}
	
	public void connectFtpServe() throws FtpException {
		try {
			ftpClient.setControlEncoding(encoding);
			ftpClient.connect(InetAddress.getByName(serverIp), port);
			ftpClient.login(username, password);
			ftpClient.enterLocalActiveMode();
			int replay = ftpClient.getReplyCode();
			if (!FTPReply.isPositiveCompletion(replay)) {
				throw new FtpException("connection error,replay for:"
						+ replay);
			}
		} catch (Exception e) {
			System.out.println("=====Connection Error====");
		    System.out.println("=====ServerIp:"+serverIp);
		    System.out.println("=====port:"+port);
		    System.out.println("=====username:"+username);
		    System.out.println("=====password:"+password);
			throw new FtpException("connection Error");
		}
	}

	
	public void closeFtpServe() {
		try {
			if (ftpClient != null && ftpClient.isConnected()){
				ftpClient.disconnect();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	
	public boolean isFileExists(String inputPath) throws FtpException{
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		try {
			ftpClient.cwd("/");
			FTPFile[] files  = ftpClient.listFiles(inputPath);
			if(files.length>0)return true;
		} catch (IOException e) {
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}
		return false;
	}
	
	/**
	 * 清除一个文件或目录
	 * @param clearFile
	 */
	private void clearTmpFile(File clearFile){
		List<File> dirList = new ArrayList<File>();
		if(clearFile.isDirectory()){
			File[] files = clearFile.listFiles();
			for(File file:files){
				if(file.isDirectory()){
					dirList.add(file);
					clearTmpFile(file);
				}
				else if(file.isFile())file.delete();
			}
			for(File dirFile:dirList){
				dirFile.delete();
			}
		}
		clearFile.delete();
		
	}
	/**
	 * 创建一个目录，支持多级创建
	 * @param dir
	 * @return
	 * @throws FtpException
	 */
	public boolean makeDirectory(String dir) throws FtpException{
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		boolean make =  false;
		try {
		   make = ftpClient.makeDirectory(dir);
		} catch (IOException e) {
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}
		return make;
	}
	
	/**
	 * 是否是目录
	 * @param path
	 * @return
	 * @throws FtpExcption
	 */
	public boolean isDirectory(String path) throws FtpException{
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		if(this.isFileExists(path)==false)throw new FtpException("file not exits:"+path);
		if(path.endsWith("/"))path = path.substring(0,path.lastIndexOf("/"));
		String fileName = path.substring(path.lastIndexOf("/")+1);
		path = path.substring(0,path.lastIndexOf("/"));
		boolean dir = false;
		try{
		ftpClient.cwd("/");
		ftpClient.cwd(path);
		FTPFile files[] = ftpClient.listFiles();
		for(FTPFile file:files){
			if(file.getName().equals(fileName)){
				if(file.isDirectory())return true;
				else return false;
			}
		}
		}catch(Exception e){
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}
		return dir;
	}
	
	/**
	 * 是否是文件
	 * @param path
	 * @return
	 * @throws FtpException
	 */
	public boolean isFile(String path) throws FtpException{
		if (!ftpClient.isConnected())
			this.connectFtpServe();
		if(this.isFileExists(path)==false)throw new FtpException("file not exits:"+path);
		if(path.endsWith("/"))path = path.substring(0,path.lastIndexOf("/"));
		String fileName = path.substring(path.lastIndexOf("/")+1);
		path = path.substring(0,path.lastIndexOf("/"));
		boolean dir = false;
		try{
		ftpClient.cwd("/");
		ftpClient.cwd(path);
		FTPFile files[] = ftpClient.listFiles();
		for(FTPFile file:files){
			if(file.getName().equals(fileName)){
				if(file.isFile())return true;
				else return false;
			}
		}
		}catch(Exception e){
			e.printStackTrace();
			throw new FtpException(e.getMessage());
		}
		return dir;
	}

	public FTPClient getFtpClient() {
		return ftpClient;
	}

	public void setFtpClient(FTPClient ftpClient) {
		this.ftpClient = ftpClient;
	}

	public String getServerIp() {
		return serverIp;
	}

	public void setServerIp(String serverIp) {
		this.serverIp = serverIp;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}
